#pragma once

#include <cassert>

namespace blu
{
	enum class type_identity
	{
		// scalars
		int_8, int_16, int_32, int_64,
		uint_8, uint_16, uint_32, uint_64,
		float_32, float_64,

		// two-components vector
		vec2_int_8, vec2_int_16, vec2_int_32, vec2_int_64,
		vec2_uint_8, vec2_uint_16, vec2_uint_32, vec2_uint_64,
		vec2_float_32, vec2_float_64,

		// three-components vector
		vec3_int_8, vec3_int_16, vec3_int_32, vec3_int_64,
		vec3_uint_8, vec3_uint_16, vec3_uint_32, vec3_uint_64,
		vec3_float_32, vec3_float_64,

		// four-components vector
		vec4_int_8, vec4_int_16, vec4_int_32, vec4_int_64,
		vec4_uint_8, vec4_uint_16, vec4_uint_32, vec4_uint_64,
		vec4_float_32, vec4_float_64,
	};

	namespace impl
	{
		typedef unsigned _type_identity_quantity;

		constexpr _type_identity_quantity _scalar_types_count = 10;

		inline void check_validity(type_identity type_)
		{
			assert(type_ <= type_identity::vec4_float_64);
		}
	}

	inline type_identity get_component_type(type_identity type_)
	{
		return static_cast<type_identity>(
			std::underlying_type_t<type_identity>(type_) % impl::_scalar_types_count);
	}

	inline std::uint32_t get_component_size(type_identity type_)
	{
		return std::underlying_type_t<type_identity>(type_) / impl::_scalar_types_count;
	}

	inline std::uint32_t get_size(type_identity type_)
	{
		switch (type_)
		{
		case blu::type_identity::int_8:
		case blu::type_identity::uint_8:
			return 1;
		case blu::type_identity::int_16:
		case blu::type_identity::uint_16:
			return 2;
		case blu::type_identity::int_32:
		case blu::type_identity::uint_32:
		case blu::type_identity::float_32:
			return 4;
		case blu::type_identity::int_64:
		case blu::type_identity::float_64:
			return 8;
		default:
			impl::check_validity(type_);
			return get_component_size(type_) * get_size(get_component_type(type_));
		}
	}

	inline bool is_int_types(type_identity type_)
	{
		return get_component_type(type_) <= type_identity::uint_64;
	}
}